Skip to content

feat(sdk): add ExecutionStore backed by DBAdapter#396

Draft
aryasaatvik wants to merge 1 commit intoRhysSullivan:mainfrom
aryasaatvik:feat/sdk-execution-store-adapter
Draft

feat(sdk): add ExecutionStore backed by DBAdapter#396
aryasaatvik wants to merge 1 commit intoRhysSullivan:mainfrom
aryasaatvik:feat/sdk-execution-store-adapter

Conversation

@aryasaatvik
Copy link
Copy Markdown
Contributor

@aryasaatvik aryasaatvik commented Apr 24, 2026

Stack

Root of a 5-PR stack — merge first. Cross-fork GitHub PRs can't use a branch on a contributor fork as a base, so these five PRs display as independent in the UI. The real dependency chain is the commit graph.

# PR Purpose
1 #396 ← you are here feat(sdk): ExecutionStore backed by DBAdapter
2 #398 feat(execution): persist engine runs + tool calls
3 #399 feat(execution): trigger propagation (CLI/HTTP/MCP)
4 #400 feat(apps): execution tables in drizzle schemas
5 #401 feat(api): /executions list/get/tool-calls endpoints

Each subsequent PR's diff includes this one's commits until this lands — after that the diffs cleanly separate.


Summary

Adds execution history persistence as a first-class concern of the SDK, wiring three tables into coreSchema (execution, execution_interaction, execution_tool_call) and exposing an ExecutionStore service on executor.executions. Generic — every backend that implements the DBAdapter contract (sqlite, postgres, memory) inherits the store for free.

What this PR adds

  • core-schema.ts: three new tables with indexes on scope_id, execution_id, tool_path, trigger_kind, created_at to back runs UI facets + timeline queries.
  • ids.ts: branded ExecutionId, ExecutionInteractionId, ExecutionToolCallId.
  • executions.ts: Execution / ExecutionInteraction / ExecutionToolCall Schema classes, status enums, create/update/list/filter input types, and the ExecutionStore Context.Tag.
  • execution-store.ts: makeExecutionStore(core) — adapter-backed implementation. Cursor pagination (cursor.ts), filter predicates (status/trigger/tool-glob/time/code/hadElicitation), and meta aggregation (facets + chart buckets).
  • executor.ts: constructs the store once per executor, exposed via executor.executions.
  • executions.test.ts: round-trip + lifecycle coverage against the in-memory adapter.

What this PR does NOT do

Follow-up PRs in the stack will:

  1. Wire the engine to call executor.executions.create/update/record*/finish* on execute/resume/tool-invoke boundaries (feat(execution): persist engine runs + tool calls via ExecutionStore #398).
  2. Thread trigger context from CLI and MCP hosts (feat(execution): propagate trigger context from CLI, HTTP, and MCP hosts #399).
  3. Add physical Drizzle tables to apps/local + apps/cloud (feat(apps): add execution tables to local + cloud drizzle schemas #400).
  4. Add /executions HTTP API endpoints (feat(api): /executions list, get, tool-calls endpoints #401).
  5. Land the runs page React UI (next).

Test plan

  • bun x vitest run — all 97 SDK tests pass, including 7 new executions.test.ts cases.
  • bun x tsc --noEmit — zero type errors in @executor/sdk.
  • Reviewer to confirm pattern fits existing connections precedent in executor.ts.

Adds execution history persistence to the core SDK surface, wiring
three new tables (`execution`, `execution_interaction`,
`execution_tool_call`) into `coreSchema` and exposing an
`ExecutionStore` service on `executor.executions`.

Changes:
- `core-schema.ts`: three new tables with `scope_id` / `execution_id`
  / `tool_path` / `trigger_kind` / `created_at` indexes for the runs
  UI's faceting + timeline queries.
- `ids.ts`: branded `ExecutionId`, `ExecutionInteractionId`,
  `ExecutionToolCallId`.
- `executions.ts`: `Execution`, `ExecutionInteraction`,
  `ExecutionToolCall` Schema classes, status enums,
  create/update/filter/sort/meta input types, and the
  `ExecutionStore` Context.Tag.
- `execution-store.ts`: `makeExecutionStore(core)` — an
  adapter-backed `ExecutionStoreService` implementation. Wraps
  `typedAdapter<CoreSchema>` for CRUD, handles cursor-based
  pagination, filter predicates (status, trigger, tool-path glob,
  time range, code substring, hadElicitation), and builds list meta
  with facets + chart buckets.
- `cursor.ts`: base64url `{ createdAt, id }` pagination cursors.
- `executor.ts`: constructs the store once per executor, exposes via
  `executor.executions`.
- `executions.test.ts`: round-trip + lifecycle coverage against the
  in-memory adapter (no migrations needed).

Follow-up work (future PRs in the stack):
- wire the engine to record runs + tool calls through this store,
- add `/executions` API endpoints, and
- land the runs UI.
aryasaatvik added a commit to aryasaatvik/executor that referenced this pull request Apr 24, 2026
Extends the existing `/executions` group with the three read
endpoints the runs UI needs. Handlers delegate to
`executor.executions.*` (added in RhysSullivan#396 / RhysSullivan#398) and scope each read
to the innermost executor scope — same rule the engine applies
when writing.

**Endpoints:**
- `GET /executions` — list with filter + cursor + optional meta.
  Query params: `limit`, `cursor`, `status` (CSV), `trigger` (CSV),
  `tool` (CSV of paths/globs), `from`/`to` (epoch ms), `after`,
  `code` (substring), `sort` (`<field>,<dir>`), `elicitation`
  (`"true"` / `"false"`). Meta bundles facets + timeline buckets;
  handler only asks for it when the request isn't paginated
  (no `cursor` / `after`), so cheap "first page, full facets" is
  the default call shape.
- `GET /executions/:id` — single execution detail +
  `pendingInteraction`. 404 on unknown id via
  `ExecutionNotFoundError` (already declared on the group).
- `GET /executions/:id/tool-calls` — tool-call timeline. 404 on
  unknown execution (guard rail so empty arrays don't mask typos).

**Response shape:** every `Date` is serialized to epoch ms at the
handler edge (`.getTime()`) so the wire format stays numeric. The
schemas in `api.ts` mirror the SDK's row projections one-to-one
modulo that transform.

**CSV + enum handling:** `splitCsv`, `parseSortParam`,
`parseElicitationParam` live in the handler file because they're
edge concerns — the SDK takes typed arrays and enums. Invalid sort
fields / directions drop back to defaults (no 400).

No new tests — the handlers are thin wrappers over the SDK store,
which already has round-trip + filter + meta coverage in
`packages/core/sdk/src/executions.test.ts`. The CSV/enum parsers
are small enough to validate by inspection.
aryasaatvik added a commit to aryasaatvik/executor that referenced this pull request Apr 24, 2026
Extends the existing `/executions` group with the three read
endpoints the runs UI needs. Handlers delegate to
`executor.executions.*` (added in RhysSullivan#396 / RhysSullivan#398) and scope each read
to the innermost executor scope — same rule the engine applies
when writing.

**Endpoints:**
- `GET /executions` — list with filter + cursor + optional meta.
  Query params: `limit`, `cursor`, `status` (CSV), `trigger` (CSV),
  `tool` (CSV of paths/globs), `from`/`to` (epoch ms), `after`,
  `code` (substring), `sort` (`<field>,<dir>`), `elicitation`
  (`"true"` / `"false"`). Meta bundles facets + timeline buckets;
  handler only asks for it when the request isn't paginated
  (no `cursor` / `after`), so cheap "first page, full facets" is
  the default call shape.
- `GET /executions/:id` — single execution detail +
  `pendingInteraction`. 404 on unknown id via
  `ExecutionNotFoundError` (already declared on the group).
- `GET /executions/:id/tool-calls` — tool-call timeline. 404 on
  unknown execution (guard rail so empty arrays don't mask typos).

**Response shape:** every `Date` is serialized to epoch ms at the
handler edge (`.getTime()`) so the wire format stays numeric. The
schemas in `api.ts` mirror the SDK's row projections one-to-one
modulo that transform.

**CSV + enum handling:** `splitCsv`, `parseSortParam`,
`parseElicitationParam` live in the handler file because they're
edge concerns — the SDK takes typed arrays and enums. Invalid sort
fields / directions drop back to defaults (no 400).

No new tests — the handlers are thin wrappers over the SDK store,
which already has round-trip + filter + meta coverage in
`packages/core/sdk/src/executions.test.ts`. The CSV/enum parsers
are small enough to validate by inspection.
aryasaatvik added a commit to aryasaatvik/executor that referenced this pull request Apr 24, 2026
Extends the existing `/executions` group with the three read
endpoints the runs UI needs. Handlers delegate to
`executor.executions.*` (added in RhysSullivan#396 / RhysSullivan#398) and scope each read
to the innermost executor scope — same rule the engine applies
when writing.

**Endpoints:**
- `GET /executions` — list with filter + cursor + optional meta.
  Query params: `limit`, `cursor`, `status` (CSV), `trigger` (CSV),
  `tool` (CSV of paths/globs), `from`/`to` (epoch ms), `after`,
  `code` (substring), `sort` (`<field>,<dir>`), `elicitation`
  (`"true"` / `"false"`). Meta bundles facets + timeline buckets;
  handler only asks for it when the request isn't paginated
  (no `cursor` / `after`), so cheap "first page, full facets" is
  the default call shape.
- `GET /executions/:id` — single execution detail +
  `pendingInteraction`. 404 on unknown id via
  `ExecutionNotFoundError` (already declared on the group).
- `GET /executions/:id/tool-calls` — tool-call timeline. 404 on
  unknown execution (guard rail so empty arrays don't mask typos).

**Response shape:** every `Date` is serialized to epoch ms at the
handler edge (`.getTime()`) so the wire format stays numeric. The
schemas in `api.ts` mirror the SDK's row projections one-to-one
modulo that transform.

**CSV + enum handling:** `splitCsv`, `parseSortParam`,
`parseElicitationParam` live in the handler file because they're
edge concerns — the SDK takes typed arrays and enums. Invalid sort
fields / directions drop back to defaults (no 400).

No new tests — the handlers are thin wrappers over the SDK store,
which already has round-trip + filter + meta coverage in
`packages/core/sdk/src/executions.test.ts`. The CSV/enum parsers
are small enough to validate by inspection.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant